---
title: "数组和字符串"
description: "从 JavaScript 开发者角度学习 C 数组和字符串。理解数组声明、指针关系、多维数组和字符串处理函数。"
---

# 数组和字符串

## 1. 介绍

### 从 JavaScript 数组到 C 数组

在 JavaScript 中，数组是动态、灵活的数据结构，可以容纳混合类型并自动调整大小。在 C 中，数组是固定大小的连续内存块，需要显式管理，但提供卓越的性能和内存效率。

C 数组是形成更复杂数据结构基础的基本构建块。理解它们对于以下方面至关重要：
- 高效的内存使用
- 性能优化
- 构建自定义数据结构
- 系统编程

> 💡 **核心概念**: C 数组只是连续的内存块。数组名实际上是指向第一个元素的指针，这使得数组和指针在 C 中密切相关。

## 2. 数组基础

### 2.1 数组声明和初始化

<UniversalEditor title="数组声明" compare={true}>
```javascript !! js
// JavaScript - 动态数组
let numbers = [1, 2, 3, 4, 5];
let mixed = [1, "hello", true, { name: "John" }];

// 数组可以增长和缩小
numbers.push(6);
numbers.pop();

// 数组长度是动态的
console.log("长度:", numbers.length);

// 访问元素
console.log("第一个元素:", numbers[0]);
console.log("最后一个元素:", numbers[numbers.length - 1]);

// 遍历
for (let i = 0; i < numbers.length; i++) {
    console.log(numbers[i]);
}

// 现代遍历方式
numbers.forEach(num => console.log(num));
```

```c !! c
#include <stdio.h>

int main() {
    // 数组声明 - 固定大小
    int numbers[5] = {1, 2, 3, 4, 5};
    
    // 数组大小必须在编译时确定
    // int dynamic_array[];  // 错误：需要指定大小
    
    // 替代初始化方式
    int arr1[5] = {1, 2, 3};  // 其余元素填充为 0
    int arr2[] = {1, 2, 3, 4, 5};  // 大小自动推断
    
    // 数组长度是固定的
    int size = sizeof(numbers) / sizeof(numbers[0]);
    printf("数组大小: %d\n", size);
    
    // 访问元素
    printf("第一个元素: %d\n", numbers[0]);
    printf("最后一个元素: %d\n", numbers[4]);
    
    // 遍历
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
    }
    
    // 使用 sizeof 进行遍历
    for (int i = 0; i < sizeof(numbers) / sizeof(numbers[0]); i++) {
        printf("元素 %d: %d\n", i, numbers[i]);
    }
    
    return 0;
}
```
</UniversalEditor>

### 2.2 数组和指针的关系

<UniversalEditor title="数组和指针" compare={true}>
```javascript !! js
// JavaScript - 数组是对象
let arr = [1, 2, 3, 4, 5];

// 数组有方法和属性
console.log("长度:", arr.length);
console.log("类型:", typeof arr);
console.log("是数组:", Array.isArray(arr));

// 数组通过引用传递
let ref = arr;
ref[0] = 100;
console.log("原始数组:", arr[0]);  // 100

// 数组方法
let doubled = arr.map(x => x * 2);
let sum = arr.reduce((a, b) => a + b, 0);
console.log("翻倍:", doubled);
console.log("总和:", sum);
```

```c !! c
#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    // 数组名是指向第一个元素的指针
    int* ptr = arr;  // 等同于: int* ptr = &arr[0];
    
    printf("数组名 (指针): %p\n", arr);
    printf("第一个元素的地址: %p\n", &arr[0]);
    printf("指针值: %p\n", ptr);
    
    // 数组索引 vs 指针算术
    printf("arr[2] = %d\n", arr[2]);
    printf("*(ptr + 2) = %d\n", *(ptr + 2));
    printf("ptr[2] = %d\n", ptr[2]);  // 指针可以使用数组语法
    
    // 通过指针修改
    ptr[1] = 100;
    printf("修改后: arr[1] = %d\n", arr[1]);
    
    // 指针算术
    for (int* p = arr; p < arr + 5; p++) {
        printf("地址 %p 的值: %d\n", p, *p);
    }
    
    // 数组大小 vs 指针大小
    printf("数组大小: %zu 字节\n", sizeof(arr));
    printf("指针大小: %zu 字节\n", sizeof(ptr));
    
    return 0;
}
```
</UniversalEditor>

### 2.3 数组边界和安全

<UniversalEditor title="数组边界安全" compare={true}>
```javascript !! js
// JavaScript - 边界检查
let arr = [1, 2, 3, 4, 5];

// 安全访问
console.log("有效访问:", arr[2]);  // 3
console.log("越界访问:", arr[10]);  // undefined (安全)

// 数组方法是安全的
arr.push(6);  // 安全添加
arr.splice(1, 1);  // 安全删除

// length 属性防止越界
for (let i = 0; i < arr.length; i++) {
    console.log(arr[i]);  // 安全遍历
}

// 现代遍历方法
arr.forEach((item, index) => {
    console.log(`索引 ${index}: ${item}`);
});
```

```c !! c
#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    
    // C 不检查数组边界！
    printf("有效访问: arr[2] = %d\n", arr[2]);
    
    // 危险：越界访问
    // 这可能导致崩溃或内存损坏
    printf("越界访问: arr[10] = %d\n", arr[10]);  // 未定义行为！
    
    // 使用边界检查的安全遍历
    int size = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < size; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    
    // 带边界检查的指针算术
    int* ptr = arr;
    for (int i = 0; i < size; i++) {
        if (ptr + i < arr + size) {  // 边界检查
            printf("*(ptr + %d) = %d\n", i, *(ptr + i));
        }
    }
    
    // 常见错误：差一错误
    // for (int i = 0; i <= size; i++)  // 错误！会越界
    
    return 0;
}
```
</UniversalEditor>

## 3. 多维数组

### 3.1 二维数组

<UniversalEditor title="二维数组" compare={true}>
```javascript !! js
// JavaScript - 嵌套数组
let matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// 访问元素
console.log("元素 [1][2]:", matrix[1][2]);  // 6

// 遍历二维数组
for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
        console.log(`matrix[${i}][${j}] = ${matrix[i][j]}`);
    }
}

// 现代遍历方式
matrix.forEach((row, i) => {
    row.forEach((element, j) => {
        console.log(`matrix[${i}][${j}] = ${element}`);
    });
});

// 动态调整大小
matrix.push([10, 11, 12]);
matrix[0].push(4);
```

```c !! c
#include <stdio.h>

int main() {
    // 二维数组声明
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    // 访问元素
    printf("元素 [1][2]: %d\n", matrix[1][2]);  // 6
    
    // 遍历二维数组
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
        }
    }
    
    // 指针数组方法（更灵活）
    int* rows[3];
    int row1[] = {1, 2, 3};
    int row2[] = {4, 5, 6};
    int row3[] = {7, 8, 9};
    
    rows[0] = row1;
    rows[1] = row2;
    rows[2] = row3;
    
    // 通过指针数组访问
    printf("使用指针数组: %d\n", rows[1][2]);  // 6
    
    // 内存布局可视化
    printf("内存地址:\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("&matrix[%d][%d] = %p\n", i, j, &matrix[i][j]);
        }
    }
    
    return 0;
}
```
</UniversalEditor>

### 3.2 高维数组

<UniversalEditor title="高维数组" compare={true}>
```javascript !! js
// JavaScript - 任意维度的嵌套数组
let cube = [
    [
        [1, 2],
        [3, 4]
    ],
    [
        [5, 6],
        [7, 8]
    ]
];

// 访问三维数组
console.log("cube[1][0][1]:", cube[1][0][1]);  // 6

// 遍历三维数组
for (let i = 0; i < cube.length; i++) {
    for (let j = 0; j < cube[i].length; j++) {
        for (let k = 0; k < cube[i][j].length; k++) {
            console.log(`cube[${i}][${j}][${k}] = ${cube[i][j][k]}`);
        }
    }
}

// 动态维度
cube.push([[9, 10], [11, 12]]);
```

```c !! c
#include <stdio.h>

int main() {
    // 三维数组声明
    int cube[2][2][2] = {
        {
            {1, 2},
            {3, 4}
        },
        {
            {5, 6},
            {7, 8}
        }
    };
    
    // 访问三维数组
    printf("cube[1][0][1]: %d\n", cube[1][0][1]);  // 6
    
    // 遍历三维数组
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 2; k++) {
                printf("cube[%d][%d][%d] = %d\n", i, j, k, cube[i][j][k]);
            }
        }
    }
    
    // 数组大小计算
    int total_elements = sizeof(cube) / sizeof(cube[0][0][0]);
    printf("总元素数: %d\n", total_elements);
    
    // 内存布局（连续）
    printf("内存布局:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 2; k++) {
                printf("&cube[%d][%d][%d] = %p\n", i, j, k, &cube[i][j][k]);
            }
        }
    }
    
    return 0;
}
```
</UniversalEditor>

## 4. 字符串处理

### 4.1 C 字符串 vs JavaScript 字符串

<UniversalEditor title="字符串比较" compare={true}>
```javascript !! js
// JavaScript - 字符串是对象
let str = "Hello, World!";
let str2 = '单引号也可以';
let template = `模板字面量: ${str}`;

// 字符串属性和方法
console.log("长度:", str.length);
console.log("大写:", str.toUpperCase());
console.log("子串:", str.substring(0, 5));
console.log("分割:", str.split(", "));

// 字符串连接
let result = str + " " + str2;
let result2 = `${str} ${str2}`;

// 字符串比较
console.log("相等:", str === "Hello, World!");
console.log("包含:", str.includes("World"));

// 字符串遍历
for (let char of str) {
    console.log(char);
}

// 字符串不可变
let upper = str.toUpperCase();
console.log("原始:", str);
console.log("修改后:", upper);
```

```c !! c
#include <stdio.h>
#include <string.h>

int main() {
    // C 字符串是字符数组
    char str[] = "Hello, World!";
    char str2[] = {'H', 'e', 'l', 'l', 'o', '\0'};  // 以 null 结尾
    
    // 字符串长度（不包括 null 终止符）
    printf("长度: %zu\n", strlen(str));
    printf("数组大小: %zu\n", sizeof(str));  // 包括 null 终止符
    
    // 字符串函数
    char upper[20];
    strcpy(upper, str);
    for (int i = 0; upper[i] != '\0'; i++) {
        if (upper[i] >= 'a' && upper[i] <= 'z') {
            upper[i] = upper[i] - 32;  // 转换为大写
        }
    }
    printf("大写: %s\n", upper);
    
    // 字符串连接
    char result[50];
    strcpy(result, str);
    strcat(result, " ");
    strcat(result, "C Programming");
    printf("连接后: %s\n", result);
    
    // 字符串比较
    printf("相等: %d\n", strcmp(str, "Hello, World!") == 0);
    printf("包含 'World': %s\n", strstr(str, "World") ? "是" : "否");
    
    // 字符串遍历
    for (int i = 0; str[i] != '\0'; i++) {
        printf("str[%d] = '%c'\n", i, str[i]);
    }
    
    // null 终止符的重要性
    printf("null 终止符: str[13] = %d\n", str[13]);
    
    return 0;
}
```
</UniversalEditor>

### 4.2 字符串函数和安全

<UniversalEditor title="字符串函数" compare={true}>
```javascript !! js
// JavaScript - 安全的字符串操作
let text = "Hello, World!";

// 安全的子串操作
console.log("子串:", text.substring(0, 5));  // "Hello"
console.log("切片:", text.slice(-6));  // "World!"

// 安全的字符串替换
let newText = text.replace("World", "JavaScript");
console.log("替换后:", newText);

// 安全的字符串分割
let parts = text.split(", ");
console.log("分割部分:", parts);

// 安全的字符串搜索
console.log("'World' 的索引:", text.indexOf("World"));
console.log("以 'Hello' 开头:", text.startsWith("Hello"));

// 安全的字符串填充
console.log("填充后:", text.padStart(20, "*"));

// 字符串长度总是安全的
console.log("长度:", text.length);
```

```c !! c
#include <stdio.h>
#include <string.h>

int main() {
    char text[] = "Hello, World!";
    char buffer[100];
    
    // 带边界检查的安全字符串复制
    strncpy(buffer, text, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';  // 确保 null 终止
    printf("复制后: %s\n", buffer);
    
    // 安全的字符串连接
    char result[50] = "Hello";
    strncat(result, ", C Programming!", sizeof(result) - strlen(result) - 1);
    printf("连接后: %s\n", result);
    
    // 安全的子串（手动实现）
    char substring[20];
    int start = 0, length = 5;
    if (start >= 0 && length > 0 && start + length <= strlen(text)) {
        strncpy(substring, text + start, length);
        substring[length] = '\0';
        printf("子串: %s\n", substring);
    }
    
    // 字符串搜索
    char* found = strstr(text, "World");
    if (found) {
        printf("找到 'World' 在位置: %ld\n", found - text);
    }
    
    // 带长度限制的字符串比较
    char compare[] = "Hello, World!";
    if (strncmp(text, compare, strlen(text)) == 0) {
        printf("字符串相等\n");
    }
    
    // 安全的字符串长度检查
    if (strlen(text) < sizeof(buffer)) {
        strcpy(buffer, text);  // 安全复制
        printf("安全复制: %s\n", buffer);
    }
    
    return 0;
}
```
</UniversalEditor>

### 4.3 字符数组和字符串字面量

<UniversalEditor title="字符数组和字符串字面量" compare={true}>
```javascript !! js
// JavaScript - 字符串字面量和操作
let literal = "这是一个字符串字面量";
let template = `模板字面量包含 ${literal}`;
let multiLine = `
    这是一个
    多行字符串
`;

// 字符串方法
console.log("原始:", literal);
console.log("大写:", literal.toUpperCase());
console.log("小写:", literal.toLowerCase());

// 字符访问
console.log("第一个字符:", literal[0]);
console.log("最后一个字符:", literal[literal.length - 1]);

// 字符串修改（创建新字符串）
let modified = literal.replace("字符串", "修改后的");
console.log("修改后:", modified);
console.log("原始未变:", literal);

// 字符串连接
let combined = literal + " " + "连接";
console.log("连接后:", combined);
```

```c !! c
#include <stdio.h>
#include <string.h>
#include <ctype.h>

int main() {
    // 字符串字面量
    char* literal = "这是一个字符串字面量";  // 只读
    char array[] = "这是一个字符数组";  // 可修改
    
    // 字符数组操作
    char text[] = "Hello, World!";
    printf("原始: %s\n", text);
    
    // 转换为大写
    for (int i = 0; text[i] != '\0'; i++) {
        text[i] = toupper(text[i]);
    }
    printf("大写: %s\n", text);
    
    // 转换回小写
    for (int i = 0; text[i] != '\0'; i++) {
        text[i] = tolower(text[i]);
    }
    printf("小写: %s\n", text);
    
    // 字符访问
    printf("第一个字符: %c\n", text[0]);
    printf("最后一个字符: %c\n", text[strlen(text) - 1]);
    
    // 字符串字面量 vs 字符数组
    printf("字面量: %s\n", literal);
    // literal[0] = 'h';  // 错误：不能修改字符串字面量
    
    printf("数组: %s\n", array);
    array[0] = 't';  // 正确：可以修改字符数组
    printf("修改后的数组: %s\n", array);
    
    // 多行字符串（使用转义序列）
    char multiLine[] = "这是一个\n多行\n字符串";
    printf("多行:\n%s\n", multiLine);
    
    return 0;
}
```
</UniversalEditor>

## 5. 缓冲区溢出防护

### 5.1 理解缓冲区溢出

<UniversalEditor title="缓冲区溢出防护" compare={true}>
```javascript !! js
// JavaScript - 自动边界检查
let str = "Hello";
let arr = [1, 2, 3, 4, 5];

// JavaScript 防止缓冲区溢出
console.log("字符串长度:", str.length);
console.log("数组长度:", arr.length);

// 安全访问
console.log("有效访问:", str[2]);  // 'l'
console.log("越界访问:", str[10]);  // undefined (安全)

// 安全修改
str = str + " World";  // 创建新字符串
arr.push(6);  // 安全添加

// JavaScript 字符串不可变
let original = "Hello";
let modified = original.toUpperCase();
console.log("原始未变:", original);
console.log("修改后:", modified);
```

```c !! c
#include <stdio.h>
#include <string.h>

int main() {
    char buffer[10];
    char source[] = "这个字符串对缓冲区来说太长了";
    
    // 危险：缓冲区溢出
    // strcpy(buffer, source);  // 会溢出缓冲区！
    
    // 安全：有界字符串复制
    strncpy(buffer, source, sizeof(buffer) - 1);
    buffer[sizeof(buffer) - 1] = '\0';  // 确保 null 终止
    printf("安全复制: %s\n", buffer);
    
    // 安全的字符串连接
    char result[20] = "Hello";
    size_t available = sizeof(result) - strlen(result) - 1;
    strncat(result, ", World!", available);
    printf("安全连接: %s\n", result);
    
    // 安全的字符串比较
    char str1[] = "Hello";
    char str2[] = "Hello, World!";
    
    if (strncmp(str1, str2, strlen(str1)) == 0) {
        printf("字符串在 str1 长度内匹配\n");
    }
    
    // 安全的字符串长度检查
    char input[] = "很长的输入字符串";
    if (strlen(input) < sizeof(buffer)) {
        strcpy(buffer, input);  // 安全
        printf("输入适合缓冲区: %s\n", buffer);
    } else {
        printf("输入对缓冲区来说太长了\n");
    }
    
    return 0;
}
```
</UniversalEditor>

### 5.2 安全字符串函数

<UniversalEditor title="安全字符串函数" compare={true}>
```javascript !! js
// JavaScript - 内置安全
let str = "Hello, World!";

// 安全的字符串操作
let substring = str.substring(0, 5);  // "Hello"
let replaced = str.replace("World", "JavaScript");
let split = str.split(", ");

// 安全的字符串构建
let parts = ["Hello", "World", "JavaScript"];
let joined = parts.join(", ");

// 安全的字符串验证
if (str.length > 0 && str.length < 100) {
    console.log("字符串长度有效");
}

// 安全的字符串遍历
for (let i = 0; i < str.length; i++) {
    console.log(str[i]);
}

// 现代安全遍历
for (let char of str) {
    console.log(char);
}
```

```c !! c
#include <stdio.h>
#include <string.h>

// 安全字符串复制函数
void safe_strcpy(char* dest, const char* src, size_t dest_size) {
    strncpy(dest, src, dest_size - 1);
    dest[dest_size - 1] = '\0';
}

// 安全字符串连接函数
void safe_strcat(char* dest, const char* src, size_t dest_size) {
    size_t dest_len = strlen(dest);
    size_t available = dest_size - dest_len - 1;
    
    if (available > 0) {
        strncat(dest, src, available);
    }
}

int main() {
    char buffer[20];
    char source[] = "Hello, World!";
    
    // 使用安全函数
    safe_strcpy(buffer, source, sizeof(buffer));
    printf("安全复制: %s\n", buffer);
    
    safe_strcat(buffer, " 更多文本", sizeof(buffer));
    printf("安全连接: %s\n", buffer);
    
    // 安全的字符串验证
    char input[] = "测试字符串";
    if (strlen(input) > 0 && strlen(input) < sizeof(buffer)) {
        safe_strcpy(buffer, input, sizeof(buffer));
        printf("有效输入复制: %s\n", buffer);
    }
    
    // 安全的字符串构建
    char result[50] = "";
    char* parts[] = {"Hello", "World", "C", "Programming"};
    int num_parts = 4;
    
    for (int i = 0; i < num_parts; i++) {
        safe_strcat(result, parts[i], sizeof(result));
        if (i < num_parts - 1) {
            safe_strcat(result, ", ", sizeof(result));
        }
    }
    printf("构建的字符串: %s\n", result);
    
    return 0;
}
```
</UniversalEditor>

## 6. 实践示例

### 6.1 数组处理函数

<UniversalEditor title="数组处理函数" compare={true}>
```javascript !! js
// JavaScript - 数组处理
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

// 查找最大值
let max = Math.max(...numbers);
console.log("最大值:", max);

// 查找最小值
let min = Math.min(...numbers);
console.log("最小值:", min);

// 计算平均值
let sum = numbers.reduce((a, b) => a + b, 0);
let average = sum / numbers.length;
console.log("平均值:", average);

// 过滤偶数
let evens = numbers.filter(n => n % 2 === 0);
console.log("偶数:", evens);

// 映射：每个数字翻倍
let doubled = numbers.map(n => n * 2);
console.log("翻倍:", doubled);

// 反转数组
let reversed = [...numbers].reverse();
console.log("反转:", reversed);
```

```c !! c
#include <stdio.h>

// 查找数组中的最大值
int find_max(int arr[], int size) {
    if (size <= 0) return 0;
    
    int max = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    return max;
}

// 查找数组中的最小值
int find_min(int arr[], int size) {
    if (size <= 0) return 0;
    
    int min = arr[0];
    for (int i = 1; i < size; i++) {
        if (arr[i] < min) {
            min = arr[i];
        }
    }
    return min;
}

// 计算平均值
double calculate_average(int arr[], int size) {
    if (size <= 0) return 0.0;
    
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return (double)sum / size;
}

// 过滤偶数
int filter_evens(int arr[], int size, int result[]) {
    int count = 0;
    for (int i = 0; i < size; i++) {
        if (arr[i] % 2 == 0) {
            result[count++] = arr[i];
        }
    }
    return count;
}

// 每个元素翻倍
void double_elements(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;
    }
}

// 反转数组
void reverse_array(int arr[], int size) {
    for (int i = 0; i < size / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[size - 1 - i];
        arr[size - 1 - i] = temp;
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    printf("原始数组: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    // 查找最大值和最小值
    printf("最大值: %d\n", find_max(numbers, size));
    printf("最小值: %d\n", find_min(numbers, size));
    printf("平均值: %.2f\n", calculate_average(numbers, size));
    
    // 过滤偶数
    int evens[10];
    int even_count = filter_evens(numbers, size, evens);
    printf("偶数: ");
    for (int i = 0; i < even_count; i++) {
        printf("%d ", evens[i]);
    }
    printf("\n");
    
    // 元素翻倍
    int doubled[10];
    for (int i = 0; i < size; i++) {
        doubled[i] = numbers[i];
    }
    double_elements(doubled, size);
    printf("翻倍: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", doubled[i]);
    }
    printf("\n");
    
    // 反转数组
    int reversed[10];
    for (int i = 0; i < size; i++) {
        reversed[i] = numbers[i];
    }
    reverse_array(reversed, size);
    printf("反转: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", reversed[i]);
    }
    printf("\n");
    
    return 0;
}
```
</UniversalEditor>

### 6.2 字符串处理函数

<UniversalEditor title="字符串处理函数" compare={true}>
```javascript !! js
// JavaScript - 字符串处理
let text = "  Hello, World!  ";

// 去除空白
let trimmed = text.trim();
console.log("去除空白:", `"${trimmed}"`);

// 转换大小写
let upper = text.toUpperCase();
let lower = text.toLowerCase();
console.log("大写:", upper);
console.log("小写:", lower);

// 替换文本
let replaced = text.replace("World", "JavaScript");
console.log("替换后:", replaced);

// 分割和连接
let words = text.split(", ");
console.log("单词:", words);
let joined = words.join(" - ");
console.log("连接:", joined);

// 检查是否包含子串
let contains = text.includes("World");
console.log("包含 'World':", contains);

// 计算出现次数
let count = (text.match(/o/g) || []).length;
console.log("'o' 的出现次数:", count);
```

```c !! c
#include <stdio.h>
#include <string.h>
#include <ctype.h>

// 去除两端的空白字符
void trim(char* str) {
    int len = strlen(str);
    int start = 0, end = len - 1;
    
    // 找到非空白字符的开始
    while (start < len && isspace(str[start])) {
        start++;
    }
    
    // 找到非空白字符的结束
    while (end >= start && isspace(str[end])) {
        end--;
    }
    
    // 将字符串移到开头
    if (start > 0) {
        for (int i = 0; i <= end - start; i++) {
            str[i] = str[start + i];
        }
        str[end - start + 1] = '\0';
    } else if (end < len - 1) {
        str[end + 1] = '\0';
    }
}

// 转换为大写
void to_upper(char* str) {
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = toupper(str[i]);
    }
}

// 转换为小写
void to_lower(char* str) {
    for (int i = 0; str[i] != '\0'; i++) {
        str[i] = tolower(str[i]);
    }
}

// 替换子串
int replace_substring(char* str, const char* old, const char* new) {
    char* found = strstr(str, old);
    if (!found) return 0;
    
    int old_len = strlen(old);
    int new_len = strlen(new);
    int str_len = strlen(str);
    
    if (new_len > old_len) {
        // 需要移动字符
        int diff = new_len - old_len;
        for (int i = str_len; i >= found - str + old_len; i--) {
            str[i + diff] = str[i];
        }
    }
    
    strncpy(found, new, new_len);
    return 1;
}

// 计算字符出现次数
int count_char(const char* str, char ch) {
    int count = 0;
    for (int i = 0; str[i] != '\0'; i++) {
        if (str[i] == ch) {
            count++;
        }
    }
    return count;
}

// 按分隔符分割字符串
int split_string(const char* str, char delimiter, char* parts[], int max_parts) {
    int count = 0;
    int len = strlen(str);
    char* temp = malloc(len + 1);
    strcpy(temp, str);
    
    char* token = strtok(temp, &delimiter);
    while (token && count < max_parts) {
        parts[count++] = strdup(token);
        token = strtok(NULL, &delimiter);
    }
    
    free(temp);
    return count;
}

int main() {
    char text[] = "  Hello, World!  ";
    printf("原始: \"%s\"\n", text);
    
    // 去除空白
    trim(text);
    printf("去除空白: \"%s\"\n", text);
    
    // 转换大小写
    char upper[50];
    strcpy(upper, text);
    to_upper(upper);
    printf("大写: %s\n", upper);
    
    char lower[50];
    strcpy(lower, text);
    to_lower(lower);
    printf("小写: %s\n", lower);
    
    // 替换子串
    char replaced[50];
    strcpy(replaced, text);
    if (replace_substring(replaced, "World", "C Programming")) {
        printf("替换后: %s\n", replaced);
    }
    
    // 计算出现次数
    printf("'o' 的出现次数: %d\n", count_char(text, 'o'));
    
    // 分割字符串
    char* parts[10];
    int part_count = split_string(text, ',', parts, 10);
    printf("分割部分: ");
    for (int i = 0; i < part_count; i++) {
        printf("\"%s\" ", parts[i]);
        free(parts[i]);
    }
    printf("\n");
    
    return 0;
}
```
</UniversalEditor>

## 7. 练习题

### 练习 1：数组操作
创建一个 C 程序，要求：
1. 声明一个包含 10 个整数的数组
2. 用随机数填充它
3. 查找最大值、最小值和平均值
4. 按升序排序数组
5. 搜索特定值

### 练习 2：字符串操作
编写一个 C 程序，要求：
1. 从用户获取字符串输入
2. 从字符串中删除所有元音字母
3. 将结果转换为大写
4. 反转字符串
5. 计算辅音字母的数量

### 练习 3：二维数组处理
创建一个 C 程序，要求：
1. 创建一个 3x3 矩阵
2. 用数字填充它
3. 计算每行和每列的总和
4. 查找矩阵中的最大值
5. 转置矩阵

## 8. 总结

### 涵盖的核心概念
- **数组声明**: 固定大小的连续内存块
- **数组-指针关系**: 数组衰减为指针
- **多维数组**: 二维和三维数组处理
- **字符串处理**: 以 null 结尾的字符数组
- **缓冲区安全**: 防止缓冲区溢出攻击
- **字符串函数**: 安全的字符串操作

### JavaScript vs C 差异
- **动态 vs 固定大小**: JavaScript 数组可增长/缩小，C 数组固定
- **边界检查**: JavaScript 有自动边界检查，C 需要手动检查
- **字符串处理**: JavaScript 字符串是对象，C 字符串是字符数组
- **内存管理**: JavaScript 是自动的，C 需要显式管理

### 最佳实践
1. **始终检查数组边界** 在访问元素之前
2. **使用安全字符串函数** 防止缓冲区溢出
3. **正确初始化数组** 避免未定义行为
4. **验证输入** 在处理字符串之前
5. **使用 sizeof()** 安全计算数组大小
6. **正确处理 null 终止符** 在字符串中

### 下一步
在下一个模块中，我们将探索**函数和栈管理**，您将学习 C 中函数的工作原理、参数传递机制以及调用栈如何操作 - 这些概念对 JavaScript 开发者来说是隐藏的，但对理解 C 的内存模型至关重要。 